package castledefense.logic.pathfinding;

import java.util.ArrayList;
import java.util.HashMap;

import castledefense.logic.Map;
import castledefense.logic.buildings.Building;
import castledefense.logic.units.Mover;

public class LinkForPathfinding {
	
	private Map map;

	private int mapSizeX;
	private int mapSizeY;
	private int castleX;
	private int castleY;
	
	private HashMap<Mover, PositionMover> unitPositionMap = new HashMap<Mover, PositionMover>();
	private ArrayList<PositionMover> unitPositionList = new ArrayList<PositionMover>();
	
	private ArrayList<PositionBuilding> buildingPositionRemovedList = new ArrayList<PositionBuilding>();
	private ArrayList<PositionBuilding> buildingPositionAddedList = new ArrayList<PositionBuilding>();
	
	private PathfindingThreadUnit pathfindingThreadUnit;
	private PathfindingThreadBuilding pathfindingThreadBuilding;
	
	private int [][] constructionMap;
	private int [][] constructionMapBasement;
	
	//---------------------- CONSTRUCTOR - IN MAIN LOOP ----------------------------------------------
	public LinkForPathfinding(){
		
	}
	
	public void init(Map map){
		this.map = map;
		mapSizeX = map.getSizeX();
		mapSizeY = map.getSizeY();
		castleX = map.getCastle().getCellX()+2;
		castleY = map.getCastle().getCellY()+2;
		//Pathfinding thread unit
		pathfindingThreadUnit = new PathfindingThreadUnit(this);
		new Thread(pathfindingThreadUnit).start();
		//Pathfinding thread building
		pathfindingThreadBuilding = new PathfindingThreadBuilding(this);
		new Thread(pathfindingThreadBuilding).start();
		//Create construction map
		constructionMap = new int[mapSizeX][mapSizeY];
		for(int i = 0; i < mapSizeX; i++){
			for(int j=0; j < mapSizeY; j++){
				constructionMap[i][j] = map.getConstructionMapValue(i, j);
			}
		}
		//Create construction map for basement
		constructionMapBasement = new int[mapSizeX][mapSizeY];
		for(int i = 0; i < mapSizeX; i++){
			for(int j=0; j < mapSizeY; j++){
				constructionMapBasement[i][j] = map.getConstructionMapValue(i, j);
			}
		}
	}
	
	//------------------------- GETTERS AND SETTERS on constant - no synchronization ------------------
	public int getMapSizeX(){
		return mapSizeX;
	}
	
	public int getMapSizeY(){
		return mapSizeY;
	}
	
	public int getCastleX(){
		return castleX;
	}
	
	public int getCastleY(){
		return castleY;
	}
	
	
	
	
	
	//------------------------------- BUILDING METHODS -------------------------------------------------
	
	//---------------------------- METHODS FROM MAIN THREAD BUILDING ----------------------------------
	/**
	 * Called from MapUpdate.addBuilding();
	 */
	public void addBuilding(Building building){
		PositionBuilding position = new PositionBuilding(building);
		pathfindingThreadBuilding.addBuilding(position); //It's synchronized inside pathfindingThreadBuilding
	}
	
	/**
	 * Called from MapUpdate.update();
	 */
	public synchronized void updateBuilding(){ //Need synchronized with removeBuilding
		for(int i=0; i<buildingPositionRemovedList.size(); i++){
			PositionBuilding positionB = buildingPositionRemovedList.get(i);
			Building building = positionB.getBuilding();
			map.removeBasementPath(building);
		}
		buildingPositionRemovedList.clear(); //All used position should be clear at this point
		
		for(int i=0; i<buildingPositionAddedList.size(); i++){
			PositionBuilding positionB = buildingPositionAddedList.get(i);
			Building building = positionB.getBuilding();
			map.addConstruction(building);
		}
		buildingPositionAddedList.clear(); //All used position should be clear at this point
	}
	
	//---------------------------- METHODS FROM PATHFINDING THREAD BUILDING ---------------------------
	/**
	 * Called from PathfindingThreadBuilding.checkPath()
	 */
	public synchronized void removeBuilding(PositionBuilding positionB){ //Need synchronized with updateBuilding
		buildingPositionRemovedList.add(positionB);
	}
	
	/**
	 * Called from PathfindingThreadBuilding.checkPath()
	 */
	public synchronized void addBuilding(PositionBuilding positionB){ //Need synchronized with updateBuilding
		buildingPositionAddedList.add(positionB);
	}
	
	
	//------------------------------- UNIT METHODS -------------------------------------------------
	
	//----------------------------- METHODS FROM MAIN THREAD UNIT -------------------------------------
	/**
	 * Called from MapUpdate.addUnit();
	 */
	public void addMover(Mover mover){
		PositionMover position = new PositionMover(mover);
		pathfindingThreadUnit.addUnit(position); //It's synchronized inside pathfindingThreadUnit
		unitPositionMap.put(mover, position);
	}
	
	/**
	 * Called from MapUpdate.removeUnit();
	 * @param unit
	 */
	public void removeMover(Mover mover){
		PositionMover position = unitPositionMap.remove(mover);
		pathfindingThreadUnit.removeUnit(position); //It's synchronized inside pathfindingThreadUnit
	}
	
	/**
	 * Called from MapUpdate.update();
	 */
	public synchronized void updateMover(){ //Need synchornized with setPath()
		for(int i=0; i<unitPositionList.size(); i++){
			PositionMover position = unitPositionList.get(i);
			Mover mover = position.getMover();
			mover.setPath(position.getPath());
			unitPositionMap.remove(mover);
		}
		unitPositionList.clear(); //All used position should be clear at this point
	}
	
	//------------------------------ METHODS FROM PATHFINDING THREAD UNIT -----------------------------
	/**
	 * Called from PathfindingThreadUnit.update();
	 */
	public synchronized void setPath(PositionMover position){ //Need synchronized with updateUnit()
		unitPositionList.add(position);
	}
	
	
	
	
	//------------------------------- GENERAL METHODS -------------------------------------------------

	// ----------------------------- METHODS FROM MAIN THREAD -----------------------------------------
	/**
	 * Called from MapUpdate.addBuilding() and MapUpdate.removeBuilding();
	 */
	public synchronized void setConstructionMap(Building building){ //Need synchronized with getConstructionMapValue
		int x = building.getCellX();
		int y = building.getCellY();
		int size = building.getSize();
		for(int i=x; i<x+size; i++){
			for(int j=y; j<y+size; j++){
				constructionMap[i][j] = map.getConstructionMapValue(i, j);
			}
		}
	}
	
	/**
	 * Called from MapUpdate.addBasement() and MapUpdate.removeBuilding();
	 */
	public synchronized void setConstructionMapBasement(Building building){ //Need synchronized with getConstructionMapValue
		int x = building.getCellX();
		int y = building.getCellY();
		int size = building.getSize();
		for(int i=x; i<x+size; i++){
			for(int j=y; j<y+size; j++){
				constructionMapBasement[i][j] = map.getConstructionMapValue(i, j);
			}
		}
	}
	
	//----------------------------- METHODS FROM PATHFINDING THREAD BOTH ------------------------------
	/**
	 * Called from Pathfinding.generateMovementMap();
	 */
	public synchronized int getConstructionMapValue(int x, int y) { //Need synchronized with setConstructionMap;
		return constructionMap[x][y];
	}
	
	/**
	 * Called from Pathfinding.generateMovementMap();
	 */
	public synchronized int getConstructionMapBasementValue(int x, int y) { //Need synchronized with setConstructionMap;
		return constructionMapBasement[x][y];
	}

}
